package com.zc.standalone.configuration;

import com.zc.core.ProtocolSupport;
import com.zc.supports.protocol.management.ProtocolSupportDefinition;
import com.zc.supports.protocol.management.ProtocolSupportLoader;
import com.zc.supports.protocol.management.ProtocolSupportLoaderProvider;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Spring 组件协议加载器
 */
@Component
public class SpringProtocolSupportLoader implements ProtocolSupportLoader,BeanPostProcessor {

    /**
     * 存储协议加载服务商
     */
    private final Map<String, ProtocolSupportLoaderProvider> providers = new ConcurrentHashMap<>();

    /**
     * 注册协议加载服务商（添加到Map缓存）
     * @param provider 协议加载服务商
     */
    public void register(ProtocolSupportLoaderProvider provider) {
        this.providers.put(provider.getProvider(), provider);
    }

    /**
     * 加载协议
     * @param definition 协议支持参数
     * @return
     */
    @Override
    public Mono<? extends ProtocolSupport> load(ProtocolSupportDefinition definition) {
        return Mono
                // 创建新流并添加流数据（获取服务商类型）
                .justOrEmpty(this.providers.get(definition.getProvider()))
                // 当流为空时转换成异常流
                .switchIfEmpty(Mono.error(() -> new UnsupportedOperationException("unsupported provider:" + definition.getProvider())))
                // 加载协议并转换流到协议支持
                .flatMap((provider) -> provider.load(definition))
                // 转换流中的元素（重命名协议）
                .map(loaded -> new RenameProtocolSupport(definition.getId(), definition.getName(), definition.getDescription(), loaded));
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ProtocolSupportLoaderProvider) {
            register(((ProtocolSupportLoaderProvider) bean));
        }
        return bean;
    }
}